import sys
import re
import pandas as pd
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
    QTableWidget, QTableWidgetItem, QFileDialog, QMessageBox, QHeaderView,
    QStatusBar, QMenuBar, QMenu, QProgressBar
)
from PyQt6.QtCore import QTimer, QThreadPool, QRunnable, pyqtSignal, Qt, QMutex
from PyQt6.QtGui import QColor, QBrush, QAction, QIcon, QPixmap
import subprocess
import platform
from datetime import datetime
from PyQt6.QtCore import QObject


def ping_host(host):
    """优化的Ping操作"""
    param = '-n' if platform.system().lower() == 'windows' else '-c'
    command = ['ping', param, '1', '-w', '500', host]  # 减少超时时间
    try:
        output = subprocess.check_output(command, universal_newlines=True, timeout=1)
        if platform.system().lower() == 'windows':
            if 'TTL=' in output:
                for line in output.split('\n'):
                    if 'time=' in line.lower():
                        time_str = line.split('time=')[1].split('ms')[0].strip()
                        if time_str.startswith('<'):
                            time_str = '0.5'  # 处理 <1ms 的情况
                        return True, float(time_str)
                return True, 0
            return False, 0
        else:
            if ' 0% packet loss' in output:
                for line in output.split('\n'):
                    if 'time=' in line:
                        time_str = line.split('time=')[1].split(' ms')[0].strip()
                        if time_str.startswith('<'):
                            time_str = '0.5'  # 处理 <1ms 的情况
                        return True, float(time_str)
                return True, 0
            return False, 0
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
        return False, 0
    except Exception as e:
        print(f"Ping错误 {host}: {str(e)}")
        return False, 0


def is_valid_ip(ip):
    """验证IP地址格式"""
    pattern = r'^(\d{1,3}\.){3}\d{1,3}$'
    if not re.match(pattern, str(ip)):
        return False
    nums = ip.split('.')
    for num in nums:
        if not 0 <= int(num) <= 255:
            return False
    return True


class WorkerSignals(QObject):
    """工作线程信号"""
    finished = pyqtSignal(int, bool, float)


class PingWorker(QRunnable):
    """优化的Ping工作线程"""
    def __init__(self, ip, row, callback):
        super().__init__()
        self.ip = ip
        self.row = row
        self.callback = callback
        self.signals = WorkerSignals()
        self.signals.finished.connect(callback)

    def run(self):
        try:
            best_result = (False, 0)
            for _ in range(2):  # Ping 2次
                is_online, response_time = ping_host(self.ip)
                if is_online and (not best_result[0] or response_time < best_result[1]):
                    best_result = (True, response_time)
            self.signals.finished.emit(self.row, best_result[0], best_result[1])
        except Exception as e:
            print(f"Ping线程错误: {str(e)}")
            self.signals.finished.emit(self.row, False, 0)


class PingView(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle("PingView - 版本 1.1")
        self.setGeometry(100, 100, 1000, 600)

        # 创建中心部件和主布局
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        self.main_layout = QVBoxLayout(self.central_widget)

        # 初始化成员变量
        self.init_variables()

        # 创建界面组件
        self.setup_ui()

    def init_variables(self):
        """初始化成员变量"""
        self.timer = QTimer()
        self.timer.timeout.connect(self.ping_all)
        
        self.ip_list = []
        self.remarks = []
        self.descriptions = []
        
        self.thread_pool = QThreadPool()
        self.thread_pool.setMaxThreadCount(20)
        
        self.online_count = 0
        self.offline_count = 0
        self.ping_counter = 0
        self.total_ips = 0
        self.batch_size = 10
        self.is_pinging = False
        self.mutex = QMutex()  # 添加互斥锁实例

    def setup_ui(self):
        """设置界面"""
        # 创建菜单栏
        self.create_menu_bar()
        
        # 创建按钮布局
        button_layout = QHBoxLayout()
        self.create_buttons(button_layout)
        self.main_layout.addLayout(button_layout)

        # 创建进度条
        progress_layout = QHBoxLayout()
        self.create_progress_bar(progress_layout)
        self.main_layout.addLayout(progress_layout)

        # 创建表格
        self.table = QTableWidget()
        self.setup_table()
        self.main_layout.addWidget(self.table)

        # 创建状态栏
        self.create_status_bar()

    def create_buttons(self, button_layout):
        """创建并设置按钮"""
        self.import_btn = QPushButton("导入Excel")
        self.start_btn = QPushButton("开始Ping")
        self.stop_btn = QPushButton("停止")
        self.export_btn = QPushButton("导出结果")
        self.export_template_btn = QPushButton("导出模板")

        # 设置图标和提示
        self.import_btn.setIcon(QIcon.fromTheme("document-open"))
        self.start_btn.setIcon(QIcon.fromTheme("media-playback-start"))
        self.stop_btn.setIcon(QIcon.fromTheme("media-playback-stop"))
        self.export_btn.setIcon(QIcon.fromTheme("document-save"))
        self.export_template_btn.setIcon(QIcon.fromTheme("document-new"))

        # 添加到布局
        button_layout.addWidget(self.import_btn)
        button_layout.addWidget(self.start_btn)
        button_layout.addWidget(self.stop_btn)
        button_layout.addWidget(self.export_btn)
        button_layout.addWidget(self.export_template_btn)

        # 连接信号
        self.import_btn.clicked.connect(self.import_excel)
        self.start_btn.clicked.connect(self.start_ping)
        self.stop_btn.clicked.connect(self.stop_ping)
        self.export_btn.clicked.connect(self.export_results)
        self.export_template_btn.clicked.connect(self.export_template)

    def create_progress_bar(self, progress_layout):
        """创建进度条"""
        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        self.progress_bar.setStyleSheet("""
            QProgressBar {
                border: 1px solid #ccc;
                border-radius: 5px;
                text-align: center;
                height: 20px;
            }
            QProgressBar::chunk {
                background-color: #4CAF50;
                border-radius: 5px;
            }
        """)
        progress_layout.addWidget(self.progress_bar)

    def setup_table(self):
        """设置表格"""
        self.table.setColumnCount(6)
        self.table.setHorizontalHeaderLabels(
            ["IP地址", "状态", "响应时间(ms)", "最后更新时间", "描述", "备注"])

        header = self.table.horizontalHeader()
        self.table.setColumnWidth(0, 120)
        self.table.setColumnWidth(1, 80)
        self.table.setColumnWidth(2, 100)
        self.table.setColumnWidth(3, 100)
        self.table.setColumnWidth(4, 200)
        self.table.setColumnWidth(5, 150)

        header.setSectionResizeMode(4, QHeaderView.ResizeMode.Interactive)
        header.setSectionResizeMode(5, QHeaderView.ResizeMode.Interactive)

    def create_status_bar(self):
        """创建状态栏"""
        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)
        self.status_bar.showMessage("当前主题: 默认")

    def create_menu_bar(self):
        """创建菜单栏"""
        menu_bar = self.menuBar()

        # 创建主题颜色菜单
        theme_menu = menu_bar.addMenu("主题颜色")

        # 修改主题按钮
        light_theme_action = QAction("○ 浅色", self)
        dark_theme_action = QAction("○ 深色", self)
        skyblue_theme_action = QAction("○ 天蓝", self)  # 改为天蓝色

        # 添加主题颜色的背景色预览
        light_theme_action.setIcon(self.create_color_icon("white"))
        dark_theme_action.setIcon(self.create_color_icon("darkgray"))
        skyblue_theme_action.setIcon(self.create_color_icon("#87CEEB"))  # 天蓝色

        # 设置主题按钮样式和信号连接
        theme_menu.setStyleSheet("""
            QMenu::item {
                padding: 8px 25px;
                border-radius: 4px;
                margin: 2px 4px;
            }
            QMenu::item:selected {
                background-color: #e0e0e0;
            }
        """)

        # 连接信号
        light_theme_action.triggered.connect(lambda: self.set_theme("light", light_theme_action, [dark_theme_action, skyblue_theme_action]))
        dark_theme_action.triggered.connect(lambda: self.set_theme("dark", dark_theme_action, [light_theme_action, skyblue_theme_action]))
        skyblue_theme_action.triggered.connect(lambda: self.set_theme("skyblue", skyblue_theme_action, [light_theme_action, dark_theme_action]))

        theme_menu.addAction(light_theme_action)
        theme_menu.addAction(dark_theme_action)
        theme_menu.addAction(skyblue_theme_action)

        # 修改版权信息显示
        copyright_label = QAction("甘肃传德源科技工程有限公司版权所有", self)
        copyright_label.setEnabled(False)
        menu_bar.addAction(copyright_label)

    def create_color_icon(self, color):
        """创建颜色图标"""
        pixmap = QPixmap(16, 16)
        pixmap.fill(QColor(color))
        return QIcon(pixmap)

    def set_theme(self, theme, current_action, other_actions):
        """设置程序主题"""
        current_action.setText("● " + current_action.text()[2:])
        for action in other_actions:
            action.setText("○ " + action.text()[2:])

        if theme == "light":
            self.setStyleSheet("")
            self.status_bar.showMessage("当前主题: 浅色")
        elif theme == "dark":
            self.setStyleSheet("""
                QWidget {
                    background-color: #2E3440;
                    color: #D8DEE9;
                }
                QTableWidget {
                    background-color: #3B4252;
                    color: #D8DEE9;
                    gridline-color: #4C566A;
                }
                QHeaderView::section {
                    background-color: #4C566A;
                    color: #D8DEE9;
                }
                QPushButton {
                    background-color: #4C566A;
                    color: #D8DEE9;
                    border: 1px solid #5E81AC;
                    padding: 5px;
                }
                QPushButton:hover {
                    background-color: #5E81AC;
                }
            """)
            self.status_bar.showMessage("当前主题: 深色")
        elif theme == "skyblue":
            self.setStyleSheet("""
                QWidget {
                    background-color: #87CEEB;
                    color: #000000;
                }
                QTableWidget {
                    background-color: rgba(255, 255, 255, 200);
                    color: #000000;
                    gridline-color: #4F94CD;
                }
                QHeaderView::section {
                    background-color: #4F94CD;
                    color: #FFFFFF;
                }
                QPushButton {
                    background-color: #4F94CD;
                    color: #FFFFFF;
                    border: 1px solid #104E8B;
                    padding: 5px;
                }
                QPushButton:hover {
                    background-color: #104E8B;
                }
            """)
            self.status_bar.showMessage("当前主题: 天蓝")

    def export_template(self):
        """导出Excel模板"""
        file_name, _ = QFileDialog.getSaveFileName(self, "保存模板", "", "Excel Files (*.xlsx)")
        if file_name:
            example_data = {
                "IP地址": ["192.168.1.1", "192.168.1.2", "192.168.1.3"],
                "描述": ["路由器", "交换机", "服务器"],
                "备注": ["网关设备", "核心交换机", "数据库服务器"]
            }
            df = pd.DataFrame(example_data)
            try:
                df.to_excel(file_name, index=False)
                QMessageBox.information(self, "成功", "Excel模板导出成功！\n包含3行示例数据，请根据实际情况修改。")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"导出模板失败：{str(e)}")

    def import_excel(self):
        """导入Excel文件"""
        file_name, _ = QFileDialog.getOpenFileName(self, "选择Excel文件", "", "Excel Files (*.xlsx *.xls)")
        if file_name:
            try:
                df = pd.read_excel(file_name, dtype=str)

                if df.empty:
                    raise ValueError("Excel文件为空")

                # 查找所需列
                columns = {
                    'ip': ['ip', 'ip地址'],
                    'desc': ['描述'],
                    'remark': ['备注']
                }

                found_columns = {}
                for col in df.columns:
                    col_name = str(col).lower().replace(' ', '')
                    for key, keywords in columns.items():
                        if any(keyword in col_name for keyword in keywords):
                            found_columns[key] = col
                            break

                if 'ip' not in found_columns:
                    raise ValueError("未找到IP地址列")

                # 处理数据
                valid_data = []
                for i, row in df.iterrows():
                    ip = str(row[found_columns['ip']]).strip()
                    if pd.isna(ip) or not is_valid_ip(ip):
                        continue

                    desc = str(row[found_columns['desc']]) if 'desc' in found_columns else ""
                    remark = str(row[found_columns['remark']]) if 'remark' in found_columns else ""

                    if pd.isna(desc): desc = ""
                    if pd.isna(remark): remark = ""

                    valid_data.append((ip, desc, remark))

                if not valid_data:
                    raise ValueError("没有找到有效的IP地址")

                # 更新数据
                self.ip_list = [x[0] for x in valid_data]
                self.descriptions = [x[1] for x in valid_data]
                self.remarks = [x[2] for x in valid_data]

                self.update_table()
                QMessageBox.information(self, "成功", f"成功导入{len(self.ip_list)}个IP地址")

            except Exception as e:
                import traceback
                print("错误详情:", traceback.format_exc())
                QMessageBox.critical(self, "错误", f"导入失败：\n{str(e)}")

    def update_table(self):
        """更新表格数据"""
        try:
            self.table.setRowCount(len(self.ip_list))
            for row, (ip, desc, remark) in enumerate(zip(self.ip_list, self.descriptions, self.remarks)):
                self.table.setItem(row, 0, QTableWidgetItem(str(ip)))
                for col in range(1, 5):
                    self.table.setItem(row, col, QTableWidgetItem(""))
                self.table.setItem(row, 4, QTableWidgetItem(str(desc)))
                self.table.setItem(row, 5, QTableWidgetItem(str(remark)))

            # 调整列宽
            self.table.resizeColumnsToContents()

        except Exception as e:
            print(f"更新表格错误: {str(e)}")
            QMessageBox.critical(self, "错误", f"更新表格失败: {str(e)}")

    def start_ping(self):
        """开始Ping操作"""
        if not self.ip_list:
            QMessageBox.warning(self, "警告", "请先导入IP地址！")
            return

        if self.is_pinging:
            return

        self.is_pinging = True
        self.online_count = 0
        self.offline_count = 0
        self.ping_counter = 0
        self.total_ips = len(self.ip_list)

        # 重置UI
        self.progress_bar.setMaximum(self.total_ips)
        self.progress_bar.setValue(0)
        self.progress_bar.setVisible(True)

        for row in range(self.table.rowCount()):
            for col in range(1, 5):
                self.table.setItem(row, col, QTableWidgetItem(""))

        self.status_bar.showMessage("开始ping操作...")
        self.timer.start(1000)

    def stop_ping(self):
        """停止Ping操作"""
        self.is_pinging = False
        self.timer.stop()
        self.thread_pool.clear()
        self.status_bar.showMessage(f"任务已停止 - 总数: {self.total_ips} | 在线: {self.online_count} | 离线: {self.offline_count}")

    def ping_all(self):
        """批量Ping处理"""
        try:
            if not self.is_pinging or self.ping_counter >= self.total_ips:
                self.stop_ping()
                return

            start_index = self.ping_counter
            end_index = min(start_index + self.batch_size, self.total_ips)

            for row in range(start_index, end_index):
                if row < len(self.ip_list):
                    worker = PingWorker(self.ip_list[row], row, self.update_ping_result)
                    self.thread_pool.start(worker)

        except Exception as e:
            print(f"批量Ping错误: {str(e)}")
            self.stop_ping()

    def update_ping_result(self, row, is_online, response_time):
        """更新Ping结果到表格"""
        try:
            if row >= self.table.rowCount():
                return

            self.mutex.lock()
            try:
                status = "在线" if is_online else "离线"
                status_item = QTableWidgetItem(status)

                if is_online:
                    response_time = round(float(response_time), 2)
                    status_item.setForeground(QBrush(QColor("#008000")))
                    status_item.setBackground(QBrush(QColor("#E8F5E9")))
                    self.online_count += 1
                else:
                    response_time = "-"
                    status_item.setForeground(QBrush(QColor("#D32F2F")))
                    status_item.setBackground(QBrush(QColor("#FFEBEE")))
                    self.offline_count += 1

                current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                self.table.setItem(row, 1, status_item)
                self.table.setItem(row, 2, QTableWidgetItem(str(response_time)))
                self.table.setItem(row, 3, QTableWidgetItem(current_time))

                self.ping_counter += 1
                self.progress_bar.setValue(self.ping_counter)

                # 更新状态栏显示进度
                progress = (self.ping_counter / self.total_ips) * 100
                if self.ping_counter >= self.total_ips:
                    status_msg = f"任务完成 - 总数: {self.total_ips} | 在线: {self.online_count} | 离线: {self.offline_count}"
                else:
                    status_msg = f"进度: {progress:.1f}% | 在线: {self.online_count} | 离线: {self.offline_count}"
                self.status_bar.showMessage(status_msg)

                # 检查是否完成
                if self.ping_counter >= self.total_ips and self.is_pinging:
                    self.is_pinging = False
                    self.timer.stop()
                    self.thread_pool.clear()
                    # 显示最终统计结果但保持进度条可见
                    self.status_bar.showMessage(
                        f"完成 | 最终统计 - 在线: {self.online_count} | 离线: {self.offline_count}"
                    )

            finally:
                self.mutex.unlock()

        except Exception as e:
            print(f"更新Ping结果错误: {str(e)}")
            self.stop_ping()

    def export_results(self):
        """导出Ping结果到Excel"""
        file_name, _ = QFileDialog.getSaveFileName(self, "保存结果", "", "Excel Files (*.xlsx)")
        if file_name:
            try:
                data = []
                for row in range(self.table.rowCount()):
                    row_data = []
                    for col in range(self.table.columnCount()):
                        item = self.table.item(row, col)
                        row_data.append(item.text() if item else "")
                    data.append(row_data)

                df = pd.DataFrame(data, columns=["IP地址", "状态", "响应时间(ms)", "最后更新时间", "描述", "备注"])
                df.to_excel(file_name, index=False)
                QMessageBox.information(self, "成功", "Ping结果导出成功！")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"导出结果失败：{str(e)}")


if __name__ == "__main__":
    # 添加警告过滤
    import warnings
    warnings.filterwarnings("ignore", category=DeprecationWarning)
    
    app = QApplication(sys.argv)
    window = PingView()
    window.show()
    sys.exit(app.exec())